package org.summerframework.component.security.role.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.summerframework.component.mybatisplus.AbstractBasePlusService;
import org.summerframework.component.security.AdminSave;
import org.summerframework.component.security.events.RoleRelChangeEvent;
import org.summerframework.component.security.role.model.dao.UserRoleRelDAO;
import org.summerframework.component.security.role.model.domain.UserRoleRelDO;
import org.summerframework.component.security.role.service.UserRoleRelService;
import org.summerframework.component.security.role.service.UserRoleService;
import org.summerframework.component.security.role.service.dto.UserRoleDTO;
import org.summerframework.component.security.role.service.dto.UserRoleRelDTO;
import org.summerframework.component.security.role.service.dto.UserRoleRelQueryDTO;
import org.summerframework.core.util.OptionalUtils;
import org.summerframework.core.util.SpringContextUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service("userRoleRelService")
public class UserRoleRelServiceImpl extends AbstractBasePlusService<UserRoleRelDAO, UserRoleRelDTO, UserRoleRelQueryDTO, UserRoleRelDO> implements UserRoleRelService {
    @Resource
    private UserRoleRelDAO userRoleRelDAO;
    @Resource
    private UserRoleService userRoleService;

    @Resource
    private AdminSave adminSave;

    @Override
    public Wrapper<UserRoleRelDO> getWrapper(UserRoleRelQueryDTO query) {
        return Wrappers.<UserRoleRelDO>lambdaQuery()
                .eq(Objects.nonNull(query.getRoleId()), UserRoleRelDO::getRoleId, query.getRoleId())
                .orderBy(Objects.nonNull(query.getOrderById()), query.getOrderById() != null ? query.getOrderById() : false, UserRoleRelDO::getId)
                .eq(Objects.nonNull(query.getUserId()), UserRoleRelDO::getUserId, query.getUserId())
                .eq(Objects.nonNull(query.getStatus()), UserRoleRelDO::getStatus, query.getStatus())
                .eq(Objects.nonNull(query.getRoleType()), UserRoleRelDO::getRoleType, query.getRoleType())
                .eq(Objects.nonNull(query.getRoleRelId()), UserRoleRelDO::getRoleRelId, query.getRoleRelId());
    }

    @Override
    public Integer updateRoleIdsByUserId(List<Long> roleIds, Long userId) {
        return updateRoleIdsByUserId(roleIds, userId, 1, null);
    }

    @Override
    public Integer updateRoleIdsByUserId(List<Long> roleIds, Long userId, Integer type, Long relId) {
        if (roleIds == null) {
            return 0;
        }

        List<UserRoleRelDTO> userRole = selectByUserId(userId, type, relId);

        List<Long> deleteRoleIds = new ArrayList<>();

        userRole.forEach(role -> {
            if (!roleIds.contains(role.getRoleId())) {
                deleteRoleIds.add(role.getId());
            } else {
                roleIds.remove(role.getRoleId());
            }
        });

        deleteRoleIds.forEach(this::deleteById);

        saveBatch(roleIds.stream().map(roleId -> {
            UserRoleRelDO userRoleRelDTO = new UserRoleRelDO();
            UserRoleDTO userRoleDTO = OptionalUtils.get(userRoleService.selectById(roleId));
            userRoleRelDTO.setRoleCode(userRoleDTO.getCode());
            userRoleRelDTO.setRoleId(userRoleDTO.getId());
            userRoleRelDTO.setRoleName(userRoleDTO.getName());
            userRoleRelDTO.setRoleType(userRoleDTO.getType());
            userRoleRelDTO.setRoleRelId(userRoleDTO.getRelId());
            userRoleRelDTO.setUserId(userId);
            userRoleRelDTO.setStatus(userRoleDTO.getStatus());
            return userRoleRelDTO;
        }).collect(Collectors.toSet()));

        if (!deleteRoleIds.isEmpty() || !roleIds.isEmpty()) {
            SpringContextUtil.publishEvent(new RoleRelChangeEvent(BASE_OP.INSERT, userId));
        }


        return roleIds.size() + deleteRoleIds.size();
    }

    @Override
    public List<UserRoleRelDTO> selectByUserId(Long userId) {
        return selectByUserId(userId, 1, null);
    }

    @Override
    public List<UserRoleRelDTO> selectByUserId(Long userId, Integer type, Long relId) {
        List<UserRoleRelDTO> query = selectByQuery(UserRoleRelQueryDTO.builder().userId(userId).roleType(type).roleRelId(relId).isPagination(false).build());

        if (adminSave.isAdmin(userId) && query.stream().noneMatch(userRoleRelDTO -> userRoleRelDTO.getRoleId().equals(1L))) {
            UserRoleRelDTO userRoleRelDTO = new UserRoleRelDTO();
            userRoleRelDTO.setStatus(1);
            userRoleRelDTO.setRoleCode("ADMIN");
            userRoleRelDTO.setRoleId(1L);
            userRoleRelDTO.setUserId(userId);
            query.add(userRoleRelDTO);
        }

        return query;
    }

    @Override
    public Integer deleteByUserId(Long userId) {
        return deleteByUserId(userId, 1, null);
    }

    @Override
    public Integer deleteByUserId(Long userId, Integer type, Long relId) {
        return userRoleRelDAO.delete(getWrapper(UserRoleRelQueryDTO.builder().userId(userId).roleType(type).roleRelId(relId).build()));
    }

    @Override
    public Integer deleteByRoleId(Long roleId) {
        return userRoleRelDAO.delete(getWrapper(UserRoleRelQueryDTO.builder().roleId(roleId).build()));
    }

    @Override
    public Integer updateByRoleId(UserRoleRelDTO dto) {
        return userRoleRelDAO.update(fromDTO2DO(dto), Wrappers.<UserRoleRelDO>lambdaUpdate().eq(UserRoleRelDO::getRoleId, dto.getRoleId()));
    }

    @Override
    public boolean check(BASE_OP baseOp, UserRoleRelDTO baseDTO) {
        if (!(baseOp == BASE_OP.UPDATE && baseDTO.getId() == null && baseDTO.getRoleId() == null)) {
            return true;
        }

        return super.check(baseOp, baseDTO);
    }
}
